/**
 * Copyright (c) 2008 michiyasu wada
 * http://www.seyself.com/
 * 
 * Distributed under The MIT License.
 * [http://www.opensource.org/licenses/mit-license.php]
 */


import flash.events.Event;
import flash.events.IEventDispatcher;
import flash.utils.Dictionary;

class flash.events.EventDispatcher extends mx.events.EventDispatcher implements IEventDispatcher
{

    public static function initialize( target:Object ):Void
    {
        if( !_iEventDispatcher ) _iEventDispatcher = new EventDispatcher();
        target.addEventListener = _iEventDispatcher.addEventListener;
        target.hasEventListener = _iEventDispatcher.hasEventListener;
        target.removeEventListener = _iEventDispatcher.removeEventListener;
        target.dispatchEvent = _iEventDispatcher.dispatchEvent;
        target._eventScope = null;
        _global.ASSetPropFlags( target, "_eventScope" , 1 );
    }

    public function EventDispatcher( target:IEventDispatcher )
    {
        _eventScope = target || this;
    }

    public function addEventListener(type:String, listener:Function, 
        useCapture:Boolean, priority:Number, useWeakReference:Boolean):Void
    {
        useCapture = useCapture == true;
        priority = priority>>0 || 0;
        useWeakReference = useWeakReference == true;
        if (priority < 0) priority = 0;

        var _targetScope:Object = _eventScope || this;
        var events:Object = getEventContainer( _targetScope, type );
        var checkEvent = events.index.value( listener );
        if ( !checkEvent ) checkEvent = [];
        if( checkEvent[0] != useCapture && checkEvent.length!=2 ){
            var param = {
                priority: priority , 
                capture: useCapture , 
                reference: useWeakReference , 
                target: _eventScope ,
                handler: listener 
            };
            events.container.push( param );
            checkEvent.push(useCapture);
            events.index.add( listener , checkEvent );
        }
    }
    
    public function dispatchEvent(event:Event):Boolean
    {
        var _targetScope:Object = _eventScope || this;
        event["_target"] = _targetScope;
        EventDispatcher.dispatchEventChain( _targetScope, event , 2 );
        return true;
    }
    
    public function hasEventListener(type:String):Boolean
    {
        var _targetScope:Object = _eventScope || this;
        var con:Object = EventDispatcher.getContainerObject( _targetScope );
        if ( con[type] ) return ( !!con[type].container.length );
        return false;
    }
    
    public function removeEventListener(
        type:String, listener:Function, useCapture:Boolean):Void
    {
        var _targetScope:Object = _eventScope || this;
        useCapture = useCapture == true;
        var con:Object = EventDispatcher.getContainerObject( _targetScope );
        if ( con[type] ) {
            var eventList:Array = con[type].container;
            var len:Number = eventList.length;
            for (var i:Number = 0; i < len; i++ ) {
                var eventHandler:Object = eventList[i];
                if( eventHandler.handler == listener ){
                    if ( eventHandler.capture == useCapture ) {
                        eventList.splice( i , 1 );
                        var s = con[type].index.value( listener );
                        if (s[0] == useCapture) s.shift();
                        else s.pop();
                        break;
                    }
                }
            }
        }
    }
    
    public function willTrigger(type:String):Boolean
    {
        return false;
    }
    
    public function toString():String
    {
        return "[object EventDispatcher]";
    }
    
    
    //--------------------------------------------------------------------------
    //
    //  PRIVATE
    //
    //--------------------------------------------------------------------------
    
     private static var _iEventDispatcher:EventDispatcher;
    private static var _container:Dictionary;
    private var _eventScope:IEventDispatcher;
    
    /**
     * @private
     */
    public static function getEventObject(
        event:Event, target:Object, phase:Number ):Event
    {
        return event.dispatchCall( event.target, target, phase );
    }
    
    private static function dispatchEventChain(
        target:Object, event:Event, phase:Number ):Boolean
    {
        var _result:Boolean = true;
        if ( typeof(target) == "movieclip" && phase <= 2 ) {
            if( target["_parent"] ){
                _result = dispatchEventChain( target["_parent"], event, 1 );
            }
        }
        if ( _result ) {
            
            _result = callDispatchEvent( target, event, phase );
            
            if( _result ){
                if ( typeof(target) == "movieclip" &&
                    phase >= 2 && event.bubbles ) {
                    if( target["_parent"] ){
                        _result = dispatchEventChain( target["_parent"], event, 3 );
                    }
                }
            }
        }
        return _result;
    }
    
    private static function callDispatchEvent( 
        target:Object, event:Event, phase:Number ):Boolean
    {
        var type:String = event.type;
        var con:Object = EventDispatcher.getContainerObject( target );
        con = con[type];
        if ( con ) {
            var i:Number = 0;
            var eventList:Array = con.container;
            var len:Number = eventList.length;
            eventList.sortOn( ["priority", "capture"] , [2 | 16, 2] );
            var _cansel:Boolean;
            var _event:Event = getEventObject( event, target, phase );
            var firstPhase:Boolean = phase == 1;
            for ( i = 0; i < len;i++ ) {
                var eventHandler:Object = eventList[i];
                if ( firstPhase == eventHandler.capture ) {
                    eventHandler.handler.apply( target, [_event] );
                }
                if ( _event["_stopImmProp"] ) return false;
                if ( _event["_stopProp"] ) _cansel = true;
            }
            if ( _cansel ) return false;
        }
        return true;
    }
    
    private static function getEventContainer( target:Object , type:String ):Object
    {
        var con:Object = getContainerObject( target );
        if ( con[type] ) return con[type];
        con[type] = {
            index: new Dictionary() ,
            container: []
        };
        return con[type];
    }
    
    private static function getContainerObject( target:Object ):Object
    {
        if ( !_container ) _container = new Dictionary();
        var con:Object = _container.value( target );
        if ( con ) return con;
        con = { };
        _container.add( target, con );
        return con;
    }
}
